package com.cib.icarus.core.utils.dfa;


import com.cib.icarus.core.consts.IcarusGeneralConsts;

import java.util.*;

/**
 * DFA(Deterministic Finite Automaton) 确定有穷自动机
 *
 * @author goomba
 */
public class DFAProvider {

    private final Map<String, Object> dfaMap;

    private static final String IS_END_STR = "isEnd";

    public static final String QUICK_MATCH_MODE = "1";

    public static final String FULL_MATCH_MODE = "2";


    /**
     * 快速搜索，一旦匹配到关键字立马返回。
     * 例如关键字列表 [我的，我的家] 目标字符串“我的家在东北” 返回【我的】
     *
     * @param text 需处理的字符串。
     * @return 返回匹配的到的关键字列表
     */
    public List<DfaMeta> quickSearch(String text) {
        return search(text, QUICK_MATCH_MODE);
    }

    /**
     * 全量搜索，尽可能长的匹配到关键字。
     * <p>
     * 例如关键字列表 [我的，我的家] 目标字符串“我的家在东北” 返回【我的家】
     *
     * @param text 需处理的字符串
     * @return 返回匹配的到的关键字列表
     */
    public List<DfaMeta> fullSearch(String text) {
        return search(text, FULL_MATCH_MODE);
    }


    /**
     * 根据给定的文本和匹配模式，返回所有匹配的模式
     *
     * @param text      期望匹配的字符串
     * @param matchMode QUICK_MATCH_MODE/FULL_MATCH_MODE
     * @return 根据给定的文本和匹配模式，返回所有匹配到的数据
     */
    private List<DfaMeta> search(String text, String matchMode) {
        List<DfaMeta> result = new ArrayList<>();
        for (int i = 0; i < text.length(); i++) {
            int length = matchLength(text, i, matchMode);
            if (length > 0) {
                DfaMeta data = new DfaMeta(i, text.substring(i, i + length));
                result.add(data);
            }
        }
        result.sort(Comparator.comparingInt(DfaMeta::getIdx));
        return result;
    }

    /**
     * 根据给定的起始位置和匹配模式，计算匹配的长度
     * 该方法使用确定有穷自动机（DFA）来在文本中查找匹配的模式
     *
     * @param text      输入的文本，用于查找匹配模式
     * @param beginIdx  开始查找的索引位置
     * @param matchMode 匹配模式，决定何时停止匹配
     * @return 返回匹配的长度，如果没有找到有效的匹配则返回0
     */
    @SuppressWarnings("unchecked")
    private int matchLength(String text, int beginIdx, String matchMode) {
        boolean matchFlg = false;
        int matchLength = 0;
        Map<String, Object> currentMap = dfaMap;
        for (int i = beginIdx; i < text.length(); i++) {
            String key = String.valueOf(text.charAt(i));
            currentMap = (Map<String, Object>) currentMap.get(key);
            if (currentMap == null) {
                break;
            }
            matchLength++;
            if (currentMap.get(IS_END_STR).equals(IcarusGeneralConsts.STR_TO_YES)) {
                matchFlg = true;
                if (DFAProvider.QUICK_MATCH_MODE.equals(matchMode)) {
                    break;
                }
            }
        }
        if (matchLength < 2 || (!matchFlg)) {
            // single char not match!
            return 0;
        }
        return matchLength;
    }


    public static DFAProvider create(Set<String> data) {
        return new DFAProvider(data);
    }


    public static DFAProvider create(String... data) {
        return new DFAProvider(new HashSet<>(Arrays.asList(data)));
    }

    private DFAProvider(Set<String> data) {
        this.dfaMap = init(data);
    }

    /**
     * 初始化DFA（Deterministic Finite Automaton）状态转移表
     * 该方法用于根据给定的数据集构建一个DFA，用于快速关键字匹配
     *
     * @param data 关键字集合，用于构建DFA
     * @return 返回构建好的DFA表示的状态转移表
     */
    @SuppressWarnings("unchecked")
    private Map<String, Object> init(Set<String> data) {
        Map<String, Object> dfaMap = new HashMap<>(data.size());
        for (String str : data) {
            // 当前处理的状态转移表
            Map<String, Object> currentDealMap = dfaMap;

            // 遍历关键字的每个字符，构建状态转移路径
            for (int i = 0; i < str.length(); i++) {
                String key = String.valueOf(str.charAt(i));
                Map<String, Object> nextDealMap;
                if (currentDealMap.containsKey(key)) {
                    nextDealMap = (Map<String, Object>) currentDealMap.get(key);
                } else {
                    nextDealMap = new HashMap<>();
                    currentDealMap.put(key, nextDealMap);
                }

                currentDealMap = nextDealMap;

                // 已经存在数据,且该数据为结束,则跳过
                if (currentDealMap.containsKey(IS_END_STR) && currentDealMap.get(IS_END_STR).equals(IcarusGeneralConsts.STR_TO_YES)) {
                    continue;
                }
                // 判断是否为关键字的最后一个字符，是则标记为结束
                String isEndVal = (i == str.length() - 1) ? IcarusGeneralConsts.STR_TO_YES : IcarusGeneralConsts.STR_TO_NO;
                currentDealMap.put(IS_END_STR, isEndVal);
            }
        }
        return dfaMap;
    }

}
